import { ready, assert, getApiPath } from '@/lib/utils.ts';
import { setupSelect, type SelectData } from '@/components/select';
import { get, postJson } from '@/lib/request';
import * as zip from "@zip.js/zip.js";
import { filesize } from "filesize";
import { getFileIcon } from '@/lib/file-icon';
import type { FilePond } from 'filepond';
import { DjangoMessageService } from '@/lib/message';
import { resetHeaderLineHTMX } from '@/lib/utils';

import {
    tf,
} from '@/components/table/lib';

import {
    createTable,
    deleteSelectedFiles
} from '@/app/media/lib';

import {
    FileUploader
} from '@/components/file-uploader';

const fileIDInfoMap = new Map<string, Record<string, any>>();
const errorMap = new Map<string, Array<string>>();


interface User {
    id: number;
    full_name: string;
    username: string;
    is_researcher: boolean;
}

interface UserOption {
    text: string;
    value: string;
}

interface FileInfo {
    filename: string;
    fileSize: number;
    fileType?: string;
    entries?: zip.Entry[];
    serverID?: string;
}

let selectData: SelectData;
let pond: FilePond;

const messageService = new DjangoMessageService();

/**
 * Transforms a list of user objects into grouped options
 * based on whether they are researchers or normal users.
 */
function transformUsersToGroupedOptions(users: User[]): SelectData {
    // Group users into researchers and normal users
    const researchers: UserOption[] = [];
    const normalUsers: UserOption[] = [];
  
    users.forEach(user => {
        // Create the display text in format "full_name (username)"
        const option: UserOption = {
            text: `${user.full_name} (${user.username})`,
            value: user.username
        };
    
        // Add to appropriate group based on is_researcher flag
        if (user.is_researcher) {
            researchers.push(option);
        } else {
            normalUsers.push(option);
        }
    });
  
    const result: SelectData = [
        {text: '创建一个新的研究者账户', value: '####'}
    ];
  
    if (researchers.length > 0) {
        result.push({
            label: '研究者',
            options: researchers
        });
    }
  
    if (normalUsers.length > 0) {
        result.push({
            label: '普通用户（分配后会变成研究者）',
            options: normalUsers
        });
    }
  
    return result;
}

function extractResearcherNameFromFileName(filename: string) {
    filename = filename.split(".", 1)[0];
    return filename.split("-", 1)[0];
}


function renderErrorArea() {
    const errorAreaElem = document.getElementById("upload-resumes-errors-area")! as HTMLElement;
    
    if (errorMap.size === 0) {
        // no need to actually delete previous errors
        errorAreaElem.classList.add("tw:hidden");
        return;
    }
    errorAreaElem.classList.remove("tw:hidden");
    
    // delete previous errors
    const errorULElem = document.getElementById("upload-resumes-errors")! as HTMLElement;
    errorULElem.replaceChildren()
    errorULElem.innerHTML = `
<h2 class="tw:font-bold tw:text-lg">${gettext('上传的文件存在以下错误')}</h2>
`;
    
    for (const [fileID, errorStrings] of errorMap) {
        const filename = fileIDInfoMap.get(fileID)!.filename;
        const errorMsg = errorStrings.map(s => `<li>${s}</li>`).join('');
        
        errorULElem.insertAdjacentHTML("beforeend", `
<li class="tw:list-row">
    <div class="tw:font-bold">${filename}</div>
    <div><ul>${errorMsg}</ul></div>
</li>`);
    }
}

function genResumesAssignmentTableRow(
    fileID: string,
    fileInfo: FileInfo
) {
    const filename = fileInfo.filename;
    const researcherName = extractResearcherNameFromFileName(filename);
    const fileIcon = getFileIcon({
        filename: filename,
        contentType: fileInfo.fileType,
        iconClass: 'tw:size-4'
    });

    return `
<tr data-fileid="${fileID}">
    <th class='tw:flex tw:gap-2'>${fileIcon}${filename}</th>
    <th>${filesize(fileInfo.fileSize)}</th>
    <th>
        <div>
            <select data-select-researcher></select>
        </div>
    </th>
    <th><input type="text" value="${researcherName}" class="tw:input tw:input-sm tw:input-bordered tw:w-full" /></th>
</tr>
`;
    
}

function renderResumesAssignmentTable() {
    const areaElem = document.getElementById("resumes-assignment-table-area")! as HTMLElement;
    if (fileIDInfoMap.size === 0) {
        areaElem.classList.add("tw:hidden");
        return;
    }

    const tableElem = document.createElement("table");
    tableElem.classList.add("tw:table");
    tableElem.insertAdjacentHTML("beforeend", `
<colgroup>
    <col span="1" style="width: 35%;">
    <col span="1" style="width: 15%;">
    <col span="1" style="width: 30%;">
    <col span="1" style="width: 20%;">
</colgroup>
<thead>
    <tr>
        <th>${gettext("文件名")}</th>
        <th>${gettext("文件大小")}</th>
        <th>${gettext("分配给")}</th>
        <th>${gettext("新建研究者姓名(“+”号规则同文件名规则)")}</th>
    </tr>
</thead>`)
    
    const tbodyElem = document.createElement("tbody");
    for (const [fileID, fileInfo] of fileIDInfoMap.entries()) {
        if (!fileInfo.entries) {
            const rowHTML = genResumesAssignmentTableRow(fileID, fileInfo as FileInfo);
            tbodyElem.insertAdjacentHTML('beforeend', rowHTML);
        } else {
            for (const fileEntry of fileInfo.entries) {
                const rowHTML = genResumesAssignmentTableRow(fileID, {
                    filename: fileEntry.filename,
                    fileSize: fileEntry.uncompressedSize,
                });
                tbodyElem.insertAdjacentHTML('beforeend', rowHTML);
            }
        }
    }

    tableElem.appendChild(tbodyElem);
    areaElem.replaceChildren(tableElem);

    // setup select
    const selectElems = document.querySelectorAll("select[data-select-researcher]");
    selectElems.forEach(elem => {
        setupSelect(elem as HTMLSelectElement, {}, selectData);
    })
}


function setFileUploader(): void {
    const fileUploaderElement = document.querySelector("#upload-resumes-area > input");
    if (!fileUploaderElement) {
        console.error("File uploader input element not found");
        return;
    }
  
    const fileUploaderInput = fileUploaderElement as HTMLInputElement;

    const fu = new FileUploader(fileUploaderInput, [], {
        multiple: true,
        instantUpload: false,
        disableUploadButton: true,
        disablePreview: true,
        acceptedFileTypes: [
            "application/pdf",
            "application/msword",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            "image/png", "image/jpeg",
            "application/zip", "application/x-zip-compressed"
        ]
    });


    if (!fu.pond) {
        console.error("File uploader pond is not initialized");
        return;
    }

    pond = fu.pond;
    
    pond.on("addfile", async (error, file) => {
        if (error) {
            console.error("Error adding file:", error);
            return;
        }

        const baseFileInfo: FileInfo = {
            filename: file.filename,
            fileSize: file.fileSize,
            fileType: file.fileType
        };

        const fileType = file.fileType;
        switch (fileType) {
            case "application/pdf":
            case "application/msword":
            case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
            case 'image/png':
            case 'image/jpeg':
                fileIDInfoMap.set(file.id, baseFileInfo);
                break;
    
            case "application/zip":
            case "application/x-zip-compressed":
                try {
                    const zipReader = new zip.ZipReader(new zip.BlobReader(file.file));
                    const entries = await zipReader.getEntries();
                    const errorStrings: string[] = [];
          
                    for (const entry of entries) {
                        if (!/\.(pdf|doc|docx|png|jpg|jpeg)$/.test(entry.filename)) {
                            errorStrings.push(`${entry.filename} ${gettext("不是支持的文件类型")}`);
                        }
                    }
          
                    if (errorStrings.length) {
                        errorMap.set(file.id, errorStrings);
                    }
          
                    fileIDInfoMap.set(file.id, {
                        ...baseFileInfo,
                        entries: entries
                    });
                } catch (err) {
                    console.error("Error processing zip file:", err);
                    errorMap.set(file.id, [gettext("ZIP 压缩包文件读取失败")]);
                }
                break;
        
            default:
                fileIDInfoMap.set(file.id, baseFileInfo);
                errorMap.set(file.id, [gettext("不是支持的文件类型")]);
                break;
        }

        renderErrorArea();
        renderResumesAssignmentTable();
    });

    pond.on("removefile", (error, file) => {
        if (error) {
            console.error("Error removing file:", error);
            return;
        }
    
        errorMap.delete(file.id);
        fileIDInfoMap.delete(file.id);
    
        renderErrorArea();
        renderResumesAssignmentTable();
    });
}


async function setSelectData() {
    const usersInfoReq = await get("/api/user/");
    const usersInfo  = await usersInfoReq.json();

    selectData = transformUsersToGroupedOptions(usersInfo);
}

function getAssignmentInformation() {
    const fileTrs = document.querySelectorAll("#resumes-assignment-table-area tbody > tr");
    const result = new Array();
    for (const tr of Array.from(fileTrs)) {
        const fileID = (tr as HTMLElement).dataset['fileid'];
        if (!fileID) {
            console.error(`Unexpected error! Cannot find 'data-fileID' attribute in ${tr}`);
            break;
        }
        
        const serverID = fileIDInfoMap.get(fileID)!.serverID;
        const filename = tr.querySelector("th:nth-child(1)")!.textContent;
        const assignee = (tr.querySelector("th:nth-child(3) select")! as HTMLSelectElement).value;
        const newResearcherName = (tr.querySelector("th:nth-child(4) input")! as HTMLInputElement).value;
        result.push({
            serverID,
            filename,
            assignee,
            newResearcherName
        });
    }
    return result;
}

let inProcessing = false;

function submit(e: Event) {
    if (inProcessing) {
        alert(gettext("请耐心等待处理完成，不要重复点击按钮"));
    }

    inProcessing = true;
    
    const submitBtnElem = e.target as HTMLButtonElement;
    const uploadLoadingIndicator = `<span class="tw:loading tw:loading-spinner tw:loading-xs"></span>`;
    const processingLoadingIndicator = `<span class="tw:loading tw:loading-bars tw:loading-xs"></span>`;
    const errorMessageElem = document.getElementById("action-error-message") as HTMLElement;
    
    submitBtnElem.classList.remove("tw:btn-error", "tw:btn-success");
    submitBtnElem.classList.add("tw:btn-primary");

    if (errorMap.size) {
        submitBtnElem.classList.remove("tw:btn-primary");
        submitBtnElem.classList.add("tw:btn-error");
        
        errorMessageElem.classList.remove("tw:hidden");
        errorMessageElem.innerHTML = `请先处理错误！`;
        return;
    }

    submitBtnElem.innerHTML = `上传文件中，请耐心等待 ${uploadLoadingIndicator}`;
    
    
    pond.processFiles().then(async (files) => {
        errorMessageElem.classList.add("tw:hidden");
        submitBtnElem.innerHTML = `处理文件中，请耐心等待 ${processingLoadingIndicator}`;

        files.forEach(file => {
            const fileID = file.id;
            fileIDInfoMap.set(fileID, {
                ...fileIDInfoMap.get(fileID),
                serverID: file.serverId
            })
        })

        const assignInfo = getAssignmentInformation();

        try {
            await postJson(
                "/user/management/upload-researchers-resumes",
                assignInfo,
                { alertError: false }
            );
        } catch (error) {
            submitBtnElem.classList.remove("tw:btn-primary");
            submitBtnElem.setAttribute("disabled", "disabled");
            submitBtnElem.innerHTML = `处理失败，请刷新页面重新上传`;
            submitBtnElem.removeEventListener("click", submit);
            
            const message = error instanceof Error ? error.message : gettext("执行出错");
            errorMessageElem.classList.remove("tw:hidden")
            errorMessageElem.innerHTML = `处理出错。错误信息: ${message}。`;
            
            inProcessing = false;
            return;
        }
        
        submitBtnElem.classList.remove("tw:btn-primary");
        submitBtnElem.classList.add("tw:btn-success");
        submitBtnElem.innerHTML = `处理完成`;

        await messageService.addMessage({
            level: 'success',
            message: '研究者简历批量上传成功!',
        });

        window.location.href = "/user/researchers";
    }).catch((error) => {
        console.error("Error uploading file", error);
        
        errorMessageElem.classList.remove("tw:hidden");
        errorMessageElem.innerHTML = `上传出错。错误信息: ${error.error.code} ${error.error.body}。`;
        
        submitBtnElem.classList.remove("tw:btn-primary");
        submitBtnElem.setAttribute("disabled", "disabled");
        submitBtnElem.innerHTML = `处理失败，请刷新页面重新上传`;

        inProcessing = false;
    });
}

async function setupBatchUploadHTMXPage() {
    resetHeaderLineHTMX();
    
    // set up event handlers for buttons
    document.getElementById("resumes-batch-uploader-submit-btn")?.addEventListener("click", submit);

    document.getElementById("resumes-batch-uploader-cancel-btn")?.addEventListener("click", () => {
        window.location.href = "/user/management/researchers-resumes";
    });

    await setSelectData();

    // setup file uploader
    setFileUploader();
}

function htmxAfterSettleHandler(event: CustomEvent<any>) {
    if (event.detail.pathInfo.requestPath === "/user/management/upload-researchers-resumes") {
        setupBatchUploadHTMXPage();
    }
}

(function () {
    ready(() => {
        const table_elem = document.getElementById("table");
        assert(table_elem, "Try to find table_elem with ID 'table' -- not found");
        const gridApi = createTable(
            getApiPath(),
            table_elem,
            [
                {
                    ...tf("researcher_username", "研究者用户名"),
                    width: 150,
                },
                {
                    ...tf("researcher_full_name", "研究者姓名"),
                    width: 150,
                }
            ]
        );

        const delete_selected_btn_elem = document.getElementById("delete_selected_files_btn");

        delete_selected_btn_elem?.addEventListener("click", () => {
            deleteSelectedFiles(gridApi);
        });

        window.onbeforeunload = function() {
            if (pond.getFiles().length !== 0) {
                return gettext("您的修改还没保存！");
            }
            return undefined;
        }

        // HTMX event handler
        document.body.addEventListener<any>('htmx:afterSettle', htmxAfterSettleHandler);
    });
})();
